<template>
  <t-space>
    <t-button @click="handleUploadFile">上传文件</t-button>
    <t-button @click="handleUploadFolder">上传文件夹</t-button>
    <t-button @click="handleZipExport" theme="danger">压缩并导出</t-button>
    <t-button @click="handleUploadZip" theme="warning"
      >上传压缩包并展开</t-button
    >
    <t-button @click="handlePackageFileList" theme="success"
      >打包文件列表</t-button
    >
  </t-space>
  <input
    type="file"
    style="width: 0"
    webkitdirectory
    multiple
    @change="handleUploadChange"
    ref="folderRef"
    v-show="false"
  />
  <input
    type="file"
    style="width: 0"
    @change="handleUploadChange"
    ref="fileRef"
    v-show="false"
  />
  <input
    type="file"
    style="width: 0"
    @change="handleUploadZipChange"
    ref="fileZipRef"
    v-show="false"
  />
  <t-table
    :columns="tableColumns"
    :data="fileList"
    @drop="handleDrop"
    @dragenter="handleDragenter"
    ref="tableRef"
  >
    <!-- 自定义没有内容的情况 -->
    <template #empty>
      <p v-if="dragActive">释放鼠标</p>
      <p v-else>
        将 <span class="font-bold">文件</span> 或
        <span class="font-bold">文件夹</span> 拖拽 到此处上传
      </p>
    </template>
    <!-- size栏 -->
    <template #size="{ row }">
      {{ getFileSize(row.size) }}
    </template>
    <!-- 操作栏 -->
    <template #action="{ rowIndex }">
      <t-button @click="handleDeleteFile(rowIndex)" size="small" theme="danger"
        >删除</t-button
      >
    </template>
  </t-table>
  <t-dialog
    header="压缩进度"
    v-model:visible="zipInfo.zipDialogVisible"
    :footer="null"
  >
    <t-progress
      theme="line"
      :percentage="zipInfo.percentage"
      :color="customColorMethod"
    />
  </t-dialog>
</template>
<script setup>
import { onMounted, ref, reactive } from "vue";
import { MessagePlugin } from "tdesign-vue-next";
import JSZip from "jszip";

const folderRef = ref(null);
// 获取文件夹下文件的entries
const readEntriesAsync = (reader) =>
  new Promise((resolve) => {
    reader.readEntries((results) => {
      resolve(results);
    });
  });

// 将文件entries变成file
const fileEntriesToFiles = (entrie) =>
  new Promise((resolve) => {
    entrie.file((file) => {
      resolve(file);
    });
  });

// 处理拖拽文件（夹）添加
const handleFileFolderAdd = (items) => {
  for (let j = 0; j < items.length; j++) {
    const item = items[j];
    const fileEntry = item.webkitGetAsEntry();
    // 当次文件夹文件大小
    let totalSize = 0;
    // 如果是文件夹类型
    if (fileEntry.isDirectory) {
      // 文件夹类型，递归处理
      const reader = fileEntry.createReader();
      // 当前需要添加的文件夹
      let curNeedAddFolder = {
        name: fileEntry.name,
        size: 0,
        time: new Date().toLocaleString(),
      };
      // 是否是根目录
      let isRoot = true;
      // 根目录下初级结构的entries
      let rootEntries = [];
      const readEntries = async (reader) => {
        return new Promise(async (resolve, reject) => {
          const readEntriesRecursive = async (reader) => {
            try {
              const results = await readEntriesAsync(reader);
              if (isRoot) {
                rootEntries = results;
                isRoot = false;
              }
              for (let i = 0; i < results.length; i++) {
                const curFileEntry = results[i];
                if (curFileEntry.isFile) {
                  const file = await fileEntriesToFiles(curFileEntry);
                  totalSize += file.size;
                } else {
                  await readEntries(curFileEntry.createReader());
                }
              }
              console.log(totalSize, "totalSize");
              resolve(totalSize); // 当循环递归完成后，解析 Promise，并传递 totalSize
            } catch (e) {
              console.error(e, "e");
              reject(e);
            }
          };
          await readEntriesRecursive(reader);
        });
      };
      readEntries(reader).then((size) => {
        console.log(rootEntries, "rootEntries");
        fileList.value.push({
          ...curNeedAddFolder,
          size,
          fileEntry: rootEntries,
        });
      });
    }
    // 如果是文件类型
    else if (fileEntry.isFile) {
      fileEntry.file((file) => {
        handleFileAdd([file]);
      });
    }
  }
};
// 处理文件添加
const handleFileAdd = (files) => {
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    Object.assign(file, { time: new Date().toLocaleString() });
    fileList.value.push(file);
  }
  console.log(fileList.value, "fileList");
};
// 文件值改变
const handleUploadChange = (event) => {
  let files = event.target.files;
  handleFileAdd(files);
};
// 上传文件夹
const handleUploadFolder = () => {
  folderRef.value.click();
};

const fileRef = ref(null);

// 上传文件
const handleUploadFile = () => {
  fileRef.value.click();
};

// 展示的文件列表
const fileList = ref([]);

// 删除某个文件
const handleDeleteFile = (rowIndex) => {
  fileList.value.splice(rowIndex, 1);
};

// 列表信息
const tableColumns = [
  {
    title: "文件名",
    colKey: "name",
    width: 200,
  },
  {
    title: "大小",
    colKey: "size",
    width: 100,
  },
  {
    title: "上传时间",
    colKey: "time",
    width: 200,
  },
  {
    title: "操作",
    colKey: "action",
    width: 100,
  },
];

// 得到文件大小
const getFileSize = (size) => {
  let initNum = 0;
  const unit = ["Bytes", "KB", "MB", "GB", "TB"];
  const returnValFunc = () => {
    return size.toFixed(2) + " " + unit[initNum];
  };
  if (size <= 1000) {
    return returnValFunc();
  }
  while (size > 1024) {
    size /= 1024;
    initNum++;
  }
  return returnValFunc();
};

const tableRef = ref(null);
// 阻止默认行为
const handlePreventDefault = () => {
  ["dragenter", "dragover", "dragleave", "drop"].forEach((event) => {
    tableRef.value.$el.addEventListener(event, preventDefault, false);
  });
};
onMounted(() => {
  handlePreventDefault();
});

const preventDefault = (e) => {
  e.preventDefault();
  e.stopPropagation();
};

// 是否在拖拽
const dragActive = ref(false);
// 鼠标放下
const handleDrop = (e) => {
  dragActive.value = false;
  let items = e.dataTransfer.items;
  handleFileFolderAdd(items);
};
// 鼠标进入
const handleDragenter = () => {
  dragActive.value = true;
};

// 文件格式和拓展名对应关系的映射对象
const fileExtensions = {
  ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  ".docx":
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  ".pptx":
    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
  ".xls": "application/vnd.ms-excel",
  ".doc": "application/msword",
  ".pdf": "application/pdf",
  ".txt": "text/plain",
  ".jpg": "image/jpeg",
  ".png": "image/png",
  ".gif": "image/gif",
  ".bmp": "image/bmp",
  ".webp": "image/webp",
  ".svg": "image/svg+xml",
  ".mp3": "audio/mpeg",
  ".wav": "audio/wav",
  ".ogg": "audio/ogg",
  ".mp4": "video/mp4",
  ".zip": "application/zip",
  ".rar": "application/x-rar-compressed",
  ".7z": "application/x-7z-compressed",
  ".tar": "application/x-tar",
  ".gz": "application/x-gzip",
  ".bz2": "application/x-bzip2",
  ".xz": "application/x-xz",
  ".lz": "application/x-lzip",
  ".lzma": "application/x-lzma",
  ".lzo": "application/x-lzop",
  ".lrz": "application/x-lrzip",
  ".lz4": "application/x-lz4",
  ".zst": "application/x-zstd",
  ".Z": "application/x-compress",
  ".lha": "application/x-lha",
  ".arj": "application/x-arj",
  ".cpio": "application/x-cpio",
  ".shar": "application/x-shar",
  ".iso": "application/x-iso9660-image",
  ".nrg": "application/x-nrg",
  ".img": "application/x-cd-image",
  ".mdf": "application/x-mdf",
  ".ima": "application/x-ima",
  ".timg": "application/x-timg",
  ".udf": "application/x-udf",
  ".cdl": "application/x-cdlink",
  ".cdi": "application/x-cdimage",
  ".cdinst": "application/x-cdinst",
  ".cdpack": "application/x-cdpack",
  ".cdproj": "application/x-cdproj",
  ".cdrecorder": "application/x-cdrecorder",
  ".cdrw": "application/x-cdrw",
  ".cdx": "application/x-cdx",
  // 添加更多的文件格式和拓展名对应关系
};

// 上传压缩包
const handleUploadZipChange = (event) => {
  let files = event.target.files;
  const zip = new JSZip();
  // const blobFile=new Blob([files[0]],{type:'application/zip'});
  zip.loadAsync(files[0], { arraybuffer: true }).then((zipItem) => {
    Object.values(zipItem.files).forEach((zipValue) => {
      const { dir, name } = zipValue;
      // 没有层级，说明是普通文件
      if (!dir) {
        // 生成base64字符串，可以根据需求生成其他类型：arraybuffer base64 string
        const base = zipItem.file(name).async("arraybuffer");
        // 将arrayBuffer字符串转为blob对象
        base.then((base64Str) => {
          // name取值最后一个/后面的值
          const fileName = name.split("/").pop();
          // 创建Blob对象,这里的type根据后缀名可以设置,我们将常用的给列举一下
          const blobType = fileExtensions["." + name.split(".").pop()];
          console.log(blobType, "blobType");
          const blob = new Blob([base64Str], {
            type: "application/octet-stream",
          });
          // 创建File对象
          const file = new File([blob], fileName, {
            type: blobType || "application/octet-stream",
          });
          Object.assign(file, { time: new Date().toLocaleString() });
          fileList.value.push(file);
        });
      }
    });
  });
};
const fileZipRef = ref(null);
const handleUploadZip = () => {
  fileZipRef.value.click();
};

// 进度条颜色
const customColorMethod = {
  "0%": "#9EEFFF",
  "20%": "#9EEFFF",
  "40%": "#43A9F2",
  "60%": "#3884F8",
  "80%": "#716AF8",
  "100%": "#FF8EA9",
};
// 压缩包弹窗信息
const zipInfo = reactive({
  zipDialogVisible: false,
  percentage: 0,
});

// 压缩并导出
const handleZipExport = () => {
  // 如果没有文件就直接返回
  if (fileList.value.length === 0) {
    MessagePlugin.error("请先上传文件");
    return;
  }
  const zip = new JSZip();
  fileList.value.forEach((file) => {
    // 生成blob对象，避免图片压缩出问题
    const blob = new Blob([file], { type: file.type });
    zip.file(file.name, blob, { binary: true });
  });
  // 设置个延迟，压缩太快的文件，不需要进度条
  const delay = setTimeout(() => {
    zipInfo.zipDialogVisible = true;
  }, 500);
  zip
    .generateAsync({ type: "blob" }, (data) => {
      // 这里可以获取文件的压缩进度
      zipInfo.percentage = data.percent.toFixed(2);
      if (!data.currentFile) {
        clearTimeout(delay);
        zipInfo.zipDialogVisible = false;
      }
    })
    .then((content) => {
      // const fils = new File([content], "test.zip", {
      //     type: "zip",
      // });
      // console.log(fils,'fils')
      const blob = new Blob([content], { type: "application/zip" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = "test.zip";
      a.click();
    });
};

// 压缩包的实例
const zip = ref(null);

// 文件夹压缩
const addFolderToZip = async (folderPath, folder) => {
  return new Promise((resolve, reject) => {
    const handleFolderToZip = async (folderPath, folder) => {
      try {
        // 获取文件夹中的文件和子文件夹列表
        for (let i = 0; i < folder.length; i++) {
          const item = folder[i];
          if (item.isFile) {
            // 如果是文件，则将文件添加到 ZIP
            const file = await fileEntriesToFiles(item);
            zip.value.file(folderPath + "/" + file.name, file);
          } else if (item.isDirectory) {
            // 如果是子文件夹，则递归调用该函数
            zip.value.folder(folderPath + "/" + item.name);
            // 对文件夹内容进行解析
            const results = await readEntriesAsync(item.createReader());
            await addFolderToZip(folderPath + "/" + item.name, results);
          }
        }
        resolve();
      } catch (e) {
        reject(e);
      }
    };
    handleFolderToZip(folderPath, folder);
  });
};

// 进行列表压缩的函数
const handleZipFileList = () => {
  return new Promise(async (resolve) => {
    for (let i = 0; i < fileList.value.length; i++) {
      const file = fileList.value[i];
      // 如果是普通文件
      if (!file.fileEntry || file.isFile) {
        // 如果是文件，则将文件添加到 ZIP
        // 生成blob对象，避免图片压缩出问题
        const blob = new Blob([file], { type: file.type });
        // 需要加上binary:true，否则微软自带的office可能会有展示bug
        zip.value.file(file.name, blob, { binary: true });
      } else {
        // 文件夹类型的
        // 如果是文件夹，则递归调用该函数
        zip.value.folder(file.name);
        await addFolderToZip(file.name, file.fileEntry);
      }
    }
    resolve(zip.value);
  });
};

// 打包文件列表
const handlePackageFileList = () => {
  // 如果没有文件就直接返回
  if (fileList.value.length === 0) {
    MessagePlugin.error("请先上传文件");
    return;
  }
  zip.value = new JSZip();
  handleZipFileList().then(() => {
    console.log(zip.value, "zip.value");
    zip.value
      .generateAsync({
        type: "blob",
      })
      .then((content) => {
        const fils = new File([content], `${fileList.value[0].name}.zip`, {
          type: "zip",
        });
        // 生成一个url
        const url = URL.createObjectURL(fils);
        // 创建一个a标签
        const a = document.createElement("a");
        // 设置a标签的href属性
        a.href = url;
        // 设置a标签的下载属性
        a.download = `${fileList.value[0].name}.zip`;
        // 触发a标签的点击事件
        a.click();
        // 移除url
        URL.revokeObjectURL(url);
      });
  });
};
</script>
<style scoped>
.font-bold {
  font-weight: bold;
}
</style>
